#include "connection.hpp"
#include "ikcp.h"
#include "connection_mgr.h"
#include "kcp_typedef.h"

namespace KCP {
DYNAMIC_SIMPLE_IMPLEMENT(CConnection);
CConnection::CConnection(CConnectionMgr &manager) :
	m_connectionMgr(manager), m_conv(0), m_kcp(nullptr),
	m_lastRecvTime(GetTickCount()), m_session(nullptr),
	m_state(conn_state_invalid), m_connecting_time(0) {
}

CConnection::~CConnection() {
	clean();
}

void CConnection::clean() {
	if (m_kcp != nullptr) {
		ikcp_release(m_kcp);
		m_kcp = nullptr;
	}
	m_conv = 0;
}

CConnection* CConnection::create(CConnectionMgr& manager,
	uint32 conv,const udp::endpoint& endpoint) {
	CConnection *conn = CConnection::fetch_obj(manager);
	if (conn == nullptr) {
		return nullptr;
	}
	conn->init_kcp(conv);
	conn->set_endpoint(endpoint);
	return conn;
}

void CConnection::release(CConnection* conn) {
	if (conn == nullptr) {
		return;
	}
	conn->close();
	CConnection::release_obj(conn);
}

void CConnection::set_endpoint(const udp::endpoint& endpoint) {
	m_endpoint = endpoint;
}

void CConnection::init_kcp(uint32 conv) {
	m_conv = conv;
	m_kcp = ikcp_create(conv, (void*)this);
	m_kcp->output = &CConnection::udp_output;

	// set kcp no delay.
	ikcp_nodelay(m_kcp, 1, 5, 1, 1);
}

int CConnection::udp_output(const char *buf, int len, ikcpcb *kcp, void *user) {
	if (user == nullptr) {
		ADebug("find user is null");
		return 0;
	}
	((CConnection*)user)->send_udp_pack(buf,len);
	return len;
}

void CConnection::send_udp_pack(const char *buf, int len) {
	m_connectionMgr.send_udp(buf,len,m_endpoint);
}

void CConnection::SendMsg(const char *msg, int len) {
	ikcp_send(m_kcp, msg, len);
}

const udp::endpoint& CConnection::get_endpoint() const {
	return m_endpoint;
}

void CConnection::setSession(IKSession *session) {
	assert(session != nullptr && "session is null");
	if (session == nullptr) {
		ADebug("Invalid session");
		return;
	}
	m_session = session;
}

void CConnection::setState(eConnectionState state) {
	m_state = state;
}

CConnection::eConnectionState CConnection::getState() {
	return m_state;
}

void CConnection::setConnectingTime(int64 time) {
	m_connecting_time = time;
}

int64 CConnection::getConnectingTime() const {
	return m_connecting_time;
}

void CConnection::reSendEstablishPack() {
	m_connectionMgr.sendEstablishPack(this->GetConv(), this->get_endpoint());
}

void CConnection::input(const char* data, size_t len, const udp::endpoint& endpoint) {
	m_lastRecvTime = GetTickCount();
	m_endpoint = endpoint;

	ikcp_input(m_kcp, data, len);

	// Get all complete package and deal it.
	char kcpBuff[KCP_PACKET_MAX_SIZE]; int kcpByte = 0;
	for (;;) {
		if ((kcpByte = ikcp_recv(m_kcp, kcpBuff, KCP_PACKET_MAX_SIZE)) > 0) {
			// handle data return close flag, just return.
			if (m_connectionMgr.handleKCPData(this, kcpBuff, kcpByte) < 0) {
				return;
			}
		} else {
			break;
		}
	}
}

void CConnection::update(int64 clockTime) {
	ikcp_update(m_kcp, clockTime);
}

bool CConnection::isTimeout() const {
	if (0 == m_lastRecvTime) { return false; }
	return GetTickCount() - m_lastRecvTime > KCP_CONNECTION_TIMEOUT_TIME;
}

void CConnection::close() {
	m_connectionMgr.handleKCPDisconnect(this, std::string("timeout"));
}

void CConnection::Disconnect() {
	m_connectionMgr.disconnect(this, std::string("disconnect"));
}
}